/*******************************************
Author: Ihab Ali                   
email :ali00173@algonquinlive.             
 IPV4Caluclator/   8th Feb 2014
****************************************************/

package model;

import java.io.Serializable;

/** 
 * The class contains all the methods to handle all calculations 
 * @author Ihab Ali (ali00173@algonquinlive.com)
 */
public class CalculatorIPV4Model implements Serializable {

    private String majorBaseNetAddress ;            
    private String majorBaseBroadcastAddress ;       
    private String majorNetworkBroadcastAddressInBinary ;    
    private String binaryMajorNetworkAddress;        
    private String MajorNetworkMask ;                    
    private String majorNetworkMaskInBinary;               
    private char networkClass;                         
    private int requiredIPAddress [] = new int[4];
    private int prefix;
    private String subNetworkAddress ;
    private String subnetNetworkAddressInBinary ;
    private int subnetBits ;
    private String broadcastAddress ;
    private String broadcastAddressInBinary ;
    private String firstHostAddress ;
    private String firstHostInBinary ;
    private int numberofHostsPerSubnet ;
    private String lastHostAddress ;
    private String lastHostAddressInBinary ;
    private String maskForSubnet ;
    private String maskForSubnetInBinary ;
    private int numberOfUsableHosts ;
    private int usableSubnets ;
    private String wildcard ;
    private String wildcardInBinary ;
    
    /**
     * Initializes the model with the values of the four octets and prefix bits value
     * @param firstOctet
     * @param secondOctet
     * @param thirdOctet
     * @param fourthOctet
     * @param prefix
     */
    public CalculatorIPV4Model(int firstOctet, int secondOctet, int thirdOctet, int fourthOctet, int prefix){
        this.requiredIPAddress[0] = firstOctet;
        this.requiredIPAddress[1] = secondOctet;
        this.requiredIPAddress[2] = thirdOctet;
        this.requiredIPAddress[3] = fourthOctet;
        this.prefix = prefix;
    }
    /**
     * Calculates all information for the detailed and brief tables
     * by calling two functions claculateMajorNetworkInfo() ,calculateSubnetInfo()
     * This function will be called by the model to do the calculations 
     * so it's reasonable to reset all the strings here .
     */
    public void doCalculations(){
       majorBaseNetAddress ="";            
       majorBaseBroadcastAddress = "";       
       majorNetworkBroadcastAddressInBinary ="" ;    
       binaryMajorNetworkAddress = "";         
       MajorNetworkMask ="" ;                    
       majorNetworkMaskInBinary =""; 
       subNetworkAddress = "";
       subnetNetworkAddressInBinary ="";
       firstHostAddress = "";
       lastHostAddress = "";
       broadcastAddress = "";
       broadcastAddressInBinary = "";
       firstHostAddress ="";
       firstHostInBinary ="";
       lastHostAddress ="";
       lastHostAddressInBinary ="";
       maskForSubnet ="";
       maskForSubnetInBinary ="";
       wildcard ="";
       wildcardInBinary ="";
       networkClass = getNetworkClass();
       claculateMajorNetworkInfo(); /// scroll to the bottom of the class to see this function .
       calculateSubnetInfo(); ///  scroll to the bottom of the class to see this function .
    }
    
    
    /****************************************************************************************************
     * All getters and setters start here 
     * 
     *************************************************************************************************/
     /**
     * Gets the major base address
     * @return majorBaseNetAddress
     */
    public String getMajorBaseNetAddress() {
        return majorBaseNetAddress;
    }
    /**
     * Gets the major base network broadcast address
     * @return majorBaseNetAddressBCast
     */
    public String getBaseNetBCast() {
        return majorBaseBroadcastAddress;
    }
    /**
     * Gets the major base network broadcast address in binary
     * @return majorBaseNetAddressBCastBin
     */
    public String getMajorNetworkBroadcastAddressInBinary() {
        return majorNetworkBroadcastAddressInBinary;
    }
    /**
     * Gets the major base address in binary
     * @return majorBaseNetAddressBin
     */
    public String getBinaryMajorNetworkAddress() {
        return binaryMajorNetworkAddress;
    }
    /**
     * Gets the major base network mask
     * @return majorBaseNetAddressMask
     */
    public String getMajorNetworkMask() {
        return MajorNetworkMask;
    }
    /**
     * Gets the major base network mask in binary
     * @return majorBaseNetAddressMaskBin
     */
    public String getMajorNetworkMaskInBinary() {
        return majorNetworkMaskInBinary;
    }
    /**
     * Gets the host bits
     * @return number of host bits
     */
    public int getHostBits() {
        if (networkClass == 'A') {
            return 24;
        } else if (networkClass == 'B') {
            return 16;
        } else {
            return 8;
        }
    }
    /**
     * Gets the net class of the address
     * @return letter based upon first octet
     */
    public char getNetworkClass() {
        if (requiredIPAddress[0] < 128)   
            return 'A';
        if (requiredIPAddress[0] < 192)   
            return 'B';
        return 'C';
    }
    /**
     * Gets the first octet
     * @return octet[0]
     */
    public int getOctet1() {
        return requiredIPAddress[0];
    }
    /**
     * Gets the second octet
     * @return octet[1]
     */
    public int getOctet2() {
        return requiredIPAddress[1];
    }
    /**
     * Gets the third octet
     * @return octet[2]
     */
    public int getOctet3() {
        return requiredIPAddress[2];
    }
    /**
     * Gets the fourth octet
     * @return octet[3]
     */
    public int getOctet4() {
        return requiredIPAddress[3];
    }
    /**
     * Gets the prefix bits
     * @return prefix
     */
    public int getPrefix() {
        return prefix;
    }
    /**
     * Gets the subnet address
     * @return subNetworkAddress
     */
    public String getSubNetworkAddress() {
        return subNetworkAddress;
    }
    /**
     * Gets the subnet address in binary
     * @return subNetworkAddressBin
     */
    public String getSubnetNetworkAddressInBinary() {
        return subnetNetworkAddressInBinary;
    }
    /**
     * Gets the subnet bit
     * @return subnetBits
     */
    public int getSubnetBits() {
        return subnetBits;
    }
    /**
     * Gets the subnet broadcast address
     * @return broadcastAddress
     */
    public String getBroadcastAddress() {
        return broadcastAddress;
    }
    /**
     * Gets the subnet broadcast address in binary
     * @return broadcastAddressBin
     */
    public String getBroadcastAddressInBinary() {
        return broadcastAddressInBinary;
    }
    /**
     * Gets the subnet first host address
     * @return firstHostAddress
     */
    public String getFirstHostAddress() {
        return firstHostAddress;
    }
    /**
     * Gets the subnet first host address in binary
     * @return firstHostAddressBin
     */
    public String getFirstHostInBinary() {
        return firstHostInBinary;
    }
    /**
     * Gets the number of subnet hosts per subnet
     * @return numberofHostsPerSubnet
     */
    public int getNumberofHostsPerSubnet() {
        return numberofHostsPerSubnet;
    }
    /**
     * Gets the subnet last host address
     * @return lastHostAddress
     */
    public String getSubLastHost() {
        return lastHostAddress;
    }
    /**
     * Gets the subnet last host address in binary
     * @return lastHostAddressBin
     */
    public String getLastHostAddressInBinary() {
        return lastHostAddressInBinary;
    }
    /**
     * Gets the subnet mask
     * @return maskForSubnet
     */
    public String getMaskForSubnet() {
        return maskForSubnet;
    }
    /**
     * Gets the subnet mask in binary
     * @return maskForSubnetBin
     */
    public String getMaskForSubnetInBinary() {
        return maskForSubnetInBinary;
    }
    /**
     * Gets the number of usable hosts per subnet
     * @return numberOfUsableHosts
     */
    public int getNumberOfUsableHosts() {
        return numberOfUsableHosts;
    }
    /**
     * Gets the number of usable subnets
     * @return usableSubnets
     */
    public int getUsableSubnets() {
        return usableSubnets;
    }
    /**
     * Gets the number of usable host bits
     * @return number based upon networkClass
     */
    public int getUsableHosts() {
        if (networkClass == 'A') {
            return 16777214;
        } else if (networkClass == 'B') {
            return 65534;
        } else {
            return 254;
        }
    }
    /**
     * Gets the wildcard mask
     * @return wildcard
     */
    public String getWildcard() {
        return wildcard;
    }
    /**
     * Gets the wildcard mask in binary
     * @return wildcardBin
     */
    public String getWildcardInBinary() {
        return wildcardInBinary;
    }
    /**
     * Check if the address is a broadcast address
     * @return true if it is, false otherwise
     */
    public boolean isBroadcast() {
        return this.toString().equals(broadcastAddress);
    }
    /**
     * Check if the address is a default route
     * @return true if it is, false otherwise
     */
    public boolean isDefaultRoute() {
        return (requiredIPAddress[0] == 0 && requiredIPAddress[1] == 0 && requiredIPAddress[2] == 0 && requiredIPAddress[3] == 0 && prefix == 0);
    }
    /**
     * Check if the address is the first usable host address
     * @return true if it is, false otherwise
     */
    public boolean isFirstHost() {
        return this.toString().equals(firstHostAddress);
		
    }
    /**
     * Check if the address is a host address
     * @return true if it is, false otherwise
     */
    public boolean isHost() {
        return isBroadcast() == false && isNetworkAddress() == false;
    }   
    /**
     * Check if the address is the last usable host address
     * @return true if it is, false otherwise
     */
    public boolean isLastHost() {
        return this.toString().equals(lastHostAddress);
		
    }
    /**
     * Check if the address is a link local address
     * @return true if it is, false otherwise
     */
    public boolean isLinkLocal() {
        return (requiredIPAddress[0] == 169 && requiredIPAddress[1] == 254);
    }
    /**
     * Check if the address is a loopback address
     * @return true if it is, false otherwise
     */
    public boolean isLoopBack() {
        return (requiredIPAddress[0] == 127);
    }
    /**
     * Check if the address is a network address
     * @return true if it is, false otherwise
     */
    public boolean isNetworkAddress() {
        return this.toString().equals(subNetworkAddress);
    }
    /**
     * Check if the address is a private address
     * @return true if it is, false otherwise
     */
    public boolean isPrivateAddress() {
        if (requiredIPAddress[0] == 10)   
            return true;
        if (requiredIPAddress[0] == 172 && requiredIPAddress[1] >= 16 && requiredIPAddress[1] < 32)
            return true;
        return requiredIPAddress[0] == 192 && requiredIPAddress[1] == 168;
    }
    /**
     * Check if the address is a test net address
     * @return true if it is, false otherwise
     */
    public boolean isTestNet() {
        return (requiredIPAddress[0] == 192 && requiredIPAddress[1] == 0 && requiredIPAddress[2] == 2);
    }
	
    /**
     * Sets the first octet
     * @param Octet1
     */
    public void setOctet1(int Octet1) {
        this.requiredIPAddress[0] = Octet1;
    }
	
    /**
     * Sets the second octet
     * @param Octet2
     */
    public void setOctet2(int Octet2) {
        this.requiredIPAddress[1] = Octet2;
    }
	
    /**
     * Sets the third octet
     * @param Octet3
     */
    public void setOctet3(int Octet3) {
        this.requiredIPAddress[2] = Octet3;
    }
	
    /**
     * Sets the fourth octet
     * @param Octet4
     */
    public void setOctet4(int Octet4) {
        this.requiredIPAddress[3] = Octet4;
    }

    /**
     * Sets the prefix bits
     * @param prefix
     */
    public void setPrefix(int prefix) {
        this.prefix = prefix;
    }
    @Override
    public String toString() {
        return Integer.toString(requiredIPAddress[0]) + '.' + 
                Integer.toString(requiredIPAddress[1]) + '.' +
                Integer.toString(requiredIPAddress[2]) + '.' +
                Integer.toString(requiredIPAddress[3]);
    } 
    /**
     * Converts a number (or octet) into a binary string
     * @param val
     * @return a complete 8 digit binary representation of a value
     */
    private String toBinary(int val) {
        /* Turns an int into an 8-digit binary string */
        return Integer.toBinaryString(0x100 | val).substring(1);
    }
    
    /*********************************************************************************
     * getters and setters and short functions end here .
     *********************************************************************************************/
    
    
    
    /******************************************************************************
     * This function is used to calculate all the major network info required 
     * The function sets the following variables to their value 
     * binaryMajorNetworkAddress,MajorNetworkMask ,majorNetworkMaskInBinary,
     * majorBaseBroadcastAddress ,majorNetworkBroadcastAddressInBinary ,majorBaseNetAddress.
     *************************************************************************/
    
     public void claculateMajorNetworkInfo(){
        
        if(networkClass == 'A'){
                binaryMajorNetworkAddress = toBinary(requiredIPAddress[0]) + ".00000000.00000000.00000000";
                MajorNetworkMask = "255.0.0.0";
                majorNetworkMaskInBinary = "11111111.00000000.00000000.00000000";
                majorBaseBroadcastAddress = Integer.toString(requiredIPAddress[0]) + ".255.255.255";
                majorNetworkBroadcastAddressInBinary = toBinary(requiredIPAddress[0]) + ".11111111.11111111.11111111";
                majorBaseNetAddress = Integer.toString(requiredIPAddress[0]) + ".0.0.0";
        }
       if(networkClass == 'B'){
                majorBaseNetAddress = Integer.toString(requiredIPAddress[0]) + "." + Integer.toString(requiredIPAddress[1]) + ".0.0";
                binaryMajorNetworkAddress = toBinary(requiredIPAddress[0]) + "." + toBinary(requiredIPAddress[1]) + ".00000000.00000000";
                MajorNetworkMask = "255.255.0.0";
                majorNetworkMaskInBinary = "11111111.11111111.00000000.00000000";
                majorBaseBroadcastAddress = Integer.toString(requiredIPAddress[0]) + "." + Integer.toString(requiredIPAddress[1]) + ".255.255";
                majorNetworkBroadcastAddressInBinary = toBinary(requiredIPAddress[0]) + "." + toBinary(requiredIPAddress[1]) + ".11111111.11111111";
       }
       if(networkClass == 'C'){
                majorBaseNetAddress = Integer.toString(requiredIPAddress[0]) + "." + Integer.toString(requiredIPAddress[1]) + "." + Integer.toString(requiredIPAddress[2]) + ".0";
                binaryMajorNetworkAddress = toBinary(requiredIPAddress[0]) + "." + toBinary(requiredIPAddress[1]) + "." + toBinary(requiredIPAddress[2]) + ".00000000";
                MajorNetworkMask = "255.255.255.0";
                majorNetworkMaskInBinary = "11111111.11111111.11111111.00000000";
                majorBaseBroadcastAddress = Integer.toString(requiredIPAddress[0]) + "." + Integer.toString(requiredIPAddress[1]) + "." + Integer.toString(requiredIPAddress[2]) + ".255";
                majorNetworkBroadcastAddressInBinary = toBinary(requiredIPAddress[0]) + "." + toBinary(requiredIPAddress[1]) + "." + toBinary(requiredIPAddress[2]) + ".11111111";
                
        }
    }// end of function 
     
     
     
     
     
     /*********************************************************************************
      * This function calculate the subnet info of the IP provided by the user 
      * The function sets the following variables to their values :
      * maskForSubnet ,maskForSubnetInBinary ,wildcard ,wildcardInBinary,subnetBits
      * usableSubnets 
        numberofHostsPerSubnet 
        numberOfUsableHosts 
      * subNetworkAddress 
        subnetNetworkAddressInBinary 
        broadcastAddress 
        broadcastAddressInBinary 
        firstHostAddress 
        lastHostAddress 
        firstHostInBinary 
        lastHostAddressInBinary 
        * .
      * 
      *******************************************************/
     
      public void calculateSubnetInfo(){
        int tempPrefix = prefix;
        
        for (int i=0; i<=3; i++) {
            
            int octetTemp = Math.min(tempPrefix, 8);
            int calculatedOctet = (int)Math.pow(2,8-octetTemp);
            maskForSubnet += 256-calculatedOctet ;
            maskForSubnetInBinary += toBinary(256-calculatedOctet) ;
            wildcard += (calculatedOctet-1) ;
            wildcardInBinary += toBinary(calculatedOctet-1) ;
            tempPrefix -= octetTemp;
            if(i<3){
              maskForSubnet  += ".";
              maskForSubnetInBinary += ".";
              wildcard += ".";
              wildcardInBinary += ".";
          }
        }
        subnetBits = prefix + getHostBits() - 32;
        if(subnetBits < 0){
            subnetBits = 0;
        }
       
        usableSubnets = (int)Math.pow(subnetBits, 2);
        numberofHostsPerSubnet = 32 - prefix ;
        numberOfUsableHosts = (int)Math.pow(2,numberofHostsPerSubnet)-2;
        
        
        tempPrefix = prefix;
        for (int i=0; i<=3; i++){
            if (tempPrefix >= 8) {
                //No need to act on this octet , it's part of the network address . Copy the octet
                subNetworkAddress += requiredIPAddress[i];
                subnetNetworkAddressInBinary +=  toBinary(requiredIPAddress[i]) ;
                broadcastAddress += requiredIPAddress[i] ;
                broadcastAddressInBinary += toBinary(requiredIPAddress[i]) ;
            } else {
                //calculate the network address portion in this octet
                int networkPortion = 256-(int)Math.pow(2,8-tempPrefix);
                subNetworkAddress += (requiredIPAddress[i] & networkPortion);
                subnetNetworkAddressInBinary +=  toBinary(requiredIPAddress[i] & networkPortion);
                broadcastAddress += 256+(requiredIPAddress[i] | ~networkPortion) ;
                broadcastAddressInBinary += toBinary(256+(requiredIPAddress[i] | ~(networkPortion)));
            }
            
          if(i<3){
                subNetworkAddress += ".";
                subnetNetworkAddressInBinary +=  "." ;
                broadcastAddress += "." ;
                broadcastAddressInBinary +=".";
               firstHostAddress = subNetworkAddress;
               lastHostAddress = broadcastAddress;
               firstHostInBinary = subnetNetworkAddressInBinary;
               lastHostAddressInBinary = broadcastAddressInBinary;
            }
          
          if(i==3){
              int networkPortion = 256-(int)Math.pow(2,8-tempPrefix);
              firstHostAddress += (1 + (requiredIPAddress[3] & (networkPortion)));
              lastHostAddress +=  (255+(requiredIPAddress[3] | ~(networkPortion)));
              firstHostInBinary +=  toBinary(1 + (requiredIPAddress[3] & (networkPortion)));
              lastHostAddressInBinary +=  toBinary(255 + (requiredIPAddress[3] | ~(networkPortion)));
              
          }
           //grab the next octet , if prefix now is less than 8 we calculate the network bits int this octet
           tempPrefix -= Math.min(8, tempPrefix);
          
         }
              
   }// end of function 
}//end of class 
